#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define DEVICE_NAME "stack"	/* device name in /proc/devices*/

int misc_register(struct miscdevice * misc);	 	/* register driver*/
int misc_deregister(struct miscdevice * misc);		/*deregister driver*/

int push_stack(char);		/*Push data to stack*/
static char pop_stack(void);	/*Pop data from stack*/

static ssize_t stack_read(struct file *, char *, size_t, loff_t *);		/*read data from user buffer*/
static ssize_t stack_write(struct file *, const char *, size_t, loff_t *);	/*write data to user buffer*/

struct stack
{
  struct list_head list;
  char data;
};

static struct list_head head;	/*list head*/

int push_stack(char c)
{
  struct stack *tmp;
  
  tmp = (struct stack*)kmalloc(sizeof(struct stack), GFP_KERNEL);
  if (!tmp) 
  {
    printk("Can't allocate memory\n");
    return -ENOMEM;
  }
  
  tmp->data = c;
  list_add(&(tmp->list), &head);
  
  printk("stack PUSH: %c\n", c);
  
  return 0;
}

static char pop_stack(void)
{
  struct stack *tmp;
  char c;
  
  tmp = list_first_entry(&head, struct stack, list);
  c = tmp->data;
  list_del(&(tmp->list));
  kfree(tmp);
  
  printk("stack POP: %c\n", c);
  
  return c;
}

ssize_t stack_read(struct file *file, char __user *buffer, size_t count, loff_t *offset)
{
  char c;
  if (list_empty(&head)) 
    return 0;
  
  c = pop_stack();
  
  if (put_user(c, buffer)){
    printk("Can't read data from stack\n");
    return -EFAULT;
  }
  
  printk("Read: %c\n", c);
  
  return 1;
}

ssize_t stack_write(struct file *file, const char __user *buffer, size_t count, loff_t *offset)
{
  char c;
  get_user(c, buffer);
  push_stack(c);
  
  printk("Write: %c\n", c);
  
  return 1;
}

const struct file_operations stack_fops = { 
  .read = stack_read,
  .write = stack_write,
  .owner = THIS_MODULE
};

static struct miscdevice stack_dev = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = DEVICE_NAME,
  .fops = &stack_fops
};

static int __init stack_init(void)
{
  int ret;
  /*
   * Create the "stack" device in the /sys/class/misc directory.
   * Udev will automatically create the /dev/stack device using
   * the default rules.
   */
  ret = misc_register(&stack_dev);
  if (ret)
    printk(KERN_ERR "Unable to register \"Stack\" misc device\n");
  
  INIT_LIST_HEAD(&head);
  
  printk("\"Stack\" misc device register\n");
  
  return ret;
}
static void __exit stack_exit(void)
{
  misc_deregister(&stack_dev);
  
  printk("\"Stack\" misc device deregister\n");
}

module_init(stack_init);
module_exit(stack_exit);

MODULE_LICENSE( "GPL" ); 
MODULE_AUTHOR( "Andrey Kharitonov" ); 
MODULE_VERSION( "0.1.0" );